package com.bsj.travel.system.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bsj.travel.cached.redis.RedisCached;
import com.bsj.travel.common.UserCommon;
import com.bsj.travel.constant.Constants;
import com.bsj.travel.constant.NumericalConstants;
import com.bsj.travel.constant.RegularExpressionConstants;
import com.bsj.travel.def.common.DO.MenuDO;
import com.bsj.travel.def.common.DO.RoleDO;
import com.bsj.travel.def.common.DO.UserDO;
import com.bsj.travel.def.common.DO.UserProductDO;
import com.bsj.travel.def.common.DTO.web.user.UserDTO;
import com.bsj.travel.def.common.DTO.web.user.UserSearchDTO;
import com.bsj.travel.def.common.VO.SystemUser;
import com.bsj.travel.def.common.VO.TokenUser;
import com.bsj.travel.def.common.VO.UserTreeVO;
import com.bsj.travel.def.common.VO.UserVO;
import com.bsj.travel.def.global.JsonResult;
import com.bsj.travel.def.global.JsonResultEnum;
import com.bsj.travel.exception.ApiException;
import com.bsj.travel.mapper.mysql.MenuMapper;
import com.bsj.travel.mapper.mysql.RoleMapper;
import com.bsj.travel.mapper.mysql.UserMapper;
import com.bsj.travel.mapper.mysql.UserProductMapper;
import com.bsj.travel.security.service.TokenService;
import com.bsj.travel.system.service.SysUserService;
import com.bsj.travel.util.SecretKeyCreateUtil;
import com.bsj.travel.util.SecurityUtils;
import com.bsj.travel.util.ServletUtils;
import com.bsj.travel.util.ip.IpUtils;
import com.bsj.travel.util.uuid.IdUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/***
 * @description SysUserServiceImpl
 * @author LiJiaXin
 * @version 1.0.0
 * @time 2024/1/10 10:04
 **/
@Service
public class SysUserServiceImpl implements SysUserService {

    @Resource
    private UserMapper userMapper;

    @Resource
    private RoleMapper roleMapper;

    @Resource
    private MenuMapper menuMapper;

    @Resource
    private UserProductMapper userProductMapper;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private RedisCached redisCached;

    @Resource
    private UserCommon userCommon;

    /**
     * 根据用户名获取令牌信息
     *
     * @param userName
     * @return com.bsj.travel.def.global.JsonResult<com.bsj.travel.def.common.VO.TokenUser>
     * @author ljx
     * @time 2024/1/11 11:03
     */
    @Override
    public JsonResult<TokenUser> getTokenUserInfo(String userName) {
        if (StringUtils.isBlank(userName)) {
            return JsonResult.fail(JsonResultEnum.ACCOUNT_PASSWORD_NULL);
        }
        UserDO userDO = userMapper.selectUserByUserName(userName);
        if (userDO == null) {
            return JsonResult.fail(JsonResultEnum.USER_INFO_ISNULL);
        }
        Integer roleId = userDO.getRoleId();
        RoleDO roleDO = roleMapper.selectById(roleId);
        if (roleDO == null) {
            throw new ApiException(JsonResultEnum.ROLE_IS_NOT_EXIST);
        }
        List<MenuDO> menuDOS = menuMapper.listMenuByRoleId(roleId);
        if (CollectionUtils.isEmpty(menuDOS)) {
            throw new ApiException(JsonResultEnum.MENU_IS_NOT_EXIST);
        }
//        final char tag = ':';
//        Set<String> permissions = menuDOS.stream().map(MenuVO::getMenuSign).filter(menuSign ->
//                getCount(menuSign, tag) == NumericalConstants.TWO
//        ).collect(Collectors.toSet());

        TokenUser tokenUser = new TokenUser();
        tokenUser.setRoles(Set.of(roleDO.getRoleSign()));
        tokenUser.setPermissions(menuDOS.stream().map(MenuDO::getMenuSign).collect(Collectors.toSet()));
        SystemUser systemUser = new SystemUser();
        BeanUtils.copyProperties(userDO, systemUser);
        tokenUser.setSystemUser(systemUser);
        return JsonResult.success(tokenUser);
    }

    /**
     * 登录
     *
     * @param userName
     * @param passWord
     * @return com.bsj.travel.def.common.VO.TokenUser
     * @author ljx
     * @time 2024/1/10 11:17
     */
    @Override
    public TokenUser login(String userName, String passWord) {
        if (StringUtils.isAnyBlank(userName, passWord)) {
            throw new ApiException(JsonResultEnum.ACCOUNT_PASSWORD_NULL);
        }
        boolean matches = Pattern.matches(RegularExpressionConstants.USERNAME_REGEXP, userName);
        if (!matches) {
            throw new ApiException(JsonResultEnum.LOGIN_USERNAME_NOT_MATCHER);
        }
        UserDO userDO = userMapper.selectUserByUserName(userName);
        if (userDO == null) {
            throw new ApiException(JsonResultEnum.USER_NOT_EXIST);
        }
        if (!passWord.equals(userDO.getPassWord())) {
            throw new ApiException(JsonResultEnum.PASSWORD_ERROR);
        }
        Integer roleId = userDO.getRoleId();
        RoleDO roleDO = roleMapper.selectById(roleId);
        if (roleDO == null) {
            throw new ApiException(JsonResultEnum.NOT_USER_INFO_ERROR);
        }
        List<MenuDO> menuDOS = menuMapper.listMenuByRoleId(roleId);
        if (CollectionUtils.isEmpty(menuDOS)) {
            throw new ApiException(JsonResultEnum.NOT_USER_INFO_ERROR);
        }
        final char tag = ':';
        Set<String> permissions = menuDOS.stream().map(MenuDO::getMenuSign).filter(menuSign ->
                getCount(menuSign, tag) == NumericalConstants.TWO
        ).collect(Collectors.toSet());

        TokenUser tokenUser = new TokenUser();
        String uuid = IdUtils.fastUUID();
        tokenUser.setToken(uuid);
        tokenUser.setUserId(userDO.getUserId());
        tokenUser.setUserName(userDO.getUserName());
        tokenUser.setIpAddr(IpUtils.getIpAddr(ServletUtils.getRequest()));
        tokenUser.setRoles(Set.of(roleDO.getRoleSign()));
        tokenUser.setPermissions(permissions);
        SystemUser systemUser = new SystemUser();
        BeanUtils.copyProperties(userDO, systemUser);
        tokenUser.setSystemUser(systemUser);
        //缓存令牌并设置有效期
        String userKey = Constants.LOGIN_TOKEN_KEY + uuid;
        //保存1天
        int expireTime = 60 * 60 * 24;
        redisCached.set(userKey, JSON.toJSONString(tokenUser), expireTime);
        return tokenUser;
    }

    /**
     * 返回指定字符出现的次数
     *
     * @param menuSign
     * @return int
     * @author ljx
     * @time 2024/1/18 15:31
     */
    private int getCount(String menuSign, char tag) {
        int count = 0;
        for (int i = 0; i < menuSign.length(); i++) {
            if (menuSign.charAt(i) == tag) {
                count++;
            }
        }
        return count;
    }

    /**
     * 添加用户
     *
     * @param userDTO
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/10 11:18
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JsonResult addUser(UserDTO userDTO) {
//        TokenUser tokenUser = tokenService.getLoginUser(SecurityUtils.getToken());
        paramCheck(userDTO);
        //权限校验
//        List<Integer> userIds = userCommon.getUserIds(tokenUser.getUserId(), 2);
//        if (!userIds.contains(userDTO.getParentId())) {
//            throw new ApiException(JsonResultEnum.NOT_AUTH);
//        }
        UserDO adminUser = userMapper.selectOne(new QueryWrapper<UserDO>().eq("parentId", NumericalConstants.NEGATIVE_ONE));
        UserDO userDO = new UserDO();
        BeanUtils.copyProperties(userDTO, userDO);
        userDO.setParentId(adminUser.getUserId());
        userDO.setAppId(createAppId(NumericalConstants.ONE));
        userDO.setSecretKey(createAppId(NumericalConstants.TWO));
        if (userMapper.insert(userDO) > 0) {
            return JsonResult.success();
        } else {
            return JsonResult.fail();
        }
    }

    /**
     * 创建appId
     *
     * @param type
     * @return
     * @author ljx
     * @time 2024/4/7 15:59
     */
    private String createAppId(Integer type) {
        String result = "";
        while (true) {
            result = SecretKeyCreateUtil.generateRandomAppId();
            if (type.equals(NumericalConstants.ONE)) {
                if (!userMapper.exists(Wrappers.<UserDO>lambdaQuery().eq(UserDO::getAppId, result))) {
                    break;
                }
            } else {
                if (!userMapper.exists(Wrappers.<UserDO>lambdaQuery().eq(UserDO::getSecretKey, result))) {
                    break;
                }
            }
        }
        return result;
    }

    /**
     * 参数校验
     *
     * @param userDTO
     * @return void
     * @author ljx
     * @time 2024/1/10 14:20
     */
    private void paramCheck(UserDTO userDTO) {
        //手机号校验
        String phone = userDTO.getPhone();
        if (StringUtils.isNotBlank(phone)) {
            phone = phone.trim();
            if (!phone.matches(RegularExpressionConstants.PHONE_REGULAR)) {
                throw new ApiException(JsonResultEnum.PHONE_FORMAT_ERROR);
            }
        }
        //去重校验
        UserSearchDTO userSearchDTO = new UserSearchDTO();
        userSearchDTO.setUserId(userDTO.getUserId());
        userSearchDTO.setUserName(userDTO.getUserName());
        if (userMapper.countByCondition(userSearchDTO) >= 1) {
            throw new ApiException(JsonResultEnum.USER_NAME_EXIST);
        }
        userSearchDTO.setUserName(null);
        userSearchDTO.setCode(userDTO.getCode());
        if (userMapper.countByCondition(userSearchDTO) >= 1) {
            throw new ApiException(JsonResultEnum.CODE_EXIST);
        }
        //角色校验
        RoleDO roleDO = roleMapper.selectById(userDTO.getRoleId());
        if (roleDO == null) {
            throw new ApiException(JsonResultEnum.ROLE_IS_NOT_EXIST);
        }
    }

    /**
     * 修改用户
     *
     * @param userDTO
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/10 11:19
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JsonResult updateUser(UserDTO userDTO) {
        Integer userId = userDTO.getUserId();
        if (userId == null) {
            return JsonResult.fail(JsonResultEnum.PARAM_ERROR);
        }
        if (userMapper.selectById(userId) == null) {
            return JsonResult.fail(JsonResultEnum.USER_NOT_EXIST);
        }
        paramCheck(userDTO);
        UserDO userDO = new UserDO();
        BeanUtils.copyProperties(userDTO, userDO);
//        userDO.setParentId(null);
        if (userMapper.updateById(userDO) > 0) {
            return JsonResult.success();
        } else {
            return JsonResult.fail();
        }
    }

    /**
     * 根据用户id获取用户信息
     *
     * @param userId
     * @return com.bsj.travel.def.common.VO.UserVO
     * @author ljx
     * @time 2024/1/10 11:19
     */
    @Override
    public UserVO getUserInfoByUserId(Integer userId) {
        if (userId == null) {
            throw new ApiException(JsonResultEnum.PARAM_ERROR);
        }
        UserDO userDO = userMapper.selectById(userId);
        if (userDO == null) {
            throw new ApiException(JsonResultEnum.USER_NOT_EXIST);
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(userDO, userVO);
        return userVO;
    }

    /**
     * 删除用户
     *
     * @param userId
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/10 11:20
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JsonResult deleteUser(Integer userId) {
        //不能删除本身
        if (SecurityUtils.getUserId().equals(userId)) {
            return JsonResult.fail();
        }
        if (userMapper.selectById(userId) == null) {
            return JsonResult.fail(JsonResultEnum.USER_NOT_EXIST);
        }
        if (userProductMapper.exists(Wrappers.<UserProductDO>lambdaQuery().eq(UserProductDO::getUserId, userId))) {
            return JsonResult.fail(JsonResultEnum.PRODUCT_EXIST_UNDER_THE_USER);
        }
        if (userMapper.deleteById(userId) > 0) {
            return JsonResult.success();
        } else {
            return JsonResult.fail();
        }
    }

    /**
     * 用户分页
     *
     * @param userSearchDTO
     * @return com.bsj.travel.def.global.JsonResult<java.util.List < com.bsj.travel.def.common.VO.UserVO>>
     * @author ljx
     * @time 2024/1/10 11:20
     */
    @Override
    public JsonResult<List<UserVO>> pageUser(UserSearchDTO userSearchDTO) {
        List<UserVO> userVOS = new LinkedList<>();
        int total = userMapper.countByCondition(userSearchDTO);
        if (total == NumericalConstants.ZERO) {
            return JsonResult.success(userVOS, total);
        }
        // 这个不可能为空，最少有一个自己的id
        userSearchDTO.initPageNumber();
        if (userSearchDTO.getUserId() == null) {
            userVOS = userMapper.selectUserPage(userSearchDTO, null);
        } else {
            List<Integer> allSubUserIds = userCommon.getUserIds(userSearchDTO.getUserId(), 2);
            userVOS = userMapper.selectUserPage(userSearchDTO, allSubUserIds);
        }
        return JsonResult.success(userVOS, total);
    }

    /**
     * 获取用户树
     *
     * @return com.bsj.travel.def.common.VO.UserTreeVO
     * @author ljx
     * @time 2024/1/10 11:20
     */
    @Override
    public UserTreeVO getUserTree() {
        return null;
    }

    /**
     * 退出登录
     *
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/10 11:21
     */
    @Override
    public JsonResult userLogout() {
        tokenService.delLoginUser(SecurityUtils.getToken());
        return JsonResult.success();
    }

    /**
     * 修改密码
     *
     * @param passWord
     * @param newPassWord
     * @return com.bsj.travel.def.global.JsonResult
     * @author ljx
     * @time 2024/1/10 11:21
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public JsonResult updatePassWord(String passWord, String newPassWord) {
        String token = SecurityUtils.getToken();
        if (StringUtils.isBlank(passWord)) {
            return JsonResult.fail(JsonResultEnum.ACCOUNT_PASSWORD_NULL);
        }
        if (StringUtils.isBlank(newPassWord)) {
            return JsonResult.fail(JsonResultEnum.NEW_PASSWORD_NULL);
        }
        Integer userId = SecurityUtils.getUserId();
        UserDO userDO = userMapper.selectById(userId);
        if (userDO == null) {
            return JsonResult.fail(JsonResultEnum.USER_NOT_EXIST);
        }
        String oldPassWord = userDO.getPassWord();
        if (!oldPassWord.equals(passWord)) {
            return JsonResult.fail(JsonResultEnum.PASSWORD_ERROR);
        }
        UserDO updateUser = new UserDO();
        updateUser.setUserId(userId);
        updateUser.setPassWord(newPassWord);
        if (userMapper.updateById(updateUser) > 0) {
            tokenService.delLoginUser(token);
            return JsonResult.success(JsonResultEnum.LOGIN_AGAIN);
        } else {
            return JsonResult.fail();
        }
    }

    public static void main(String[] args) {
        System.out.println(SecretKeyCreateUtil.generateRandomAppId());
        System.out.println(SecretKeyCreateUtil.generateRandomAppId());

    }

}
